package com.aiyan.edu.govern.gateway.filter;


import com.aiyan.edu.framework.model.response.CommonCode;
import com.aiyan.edu.framework.model.response.ResponseResult;
import com.aiyan.edu.framework.utils.Oauth2Util;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.MetricsEndpoint;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 用户身份认证过滤器工厂
 *
 * @author: zqzhou
 * @create: 2019/12/01 21:35
 **/
@Slf4j
@Component
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public static final String Authorize_UserID = "X-Authorize-UserID";
    public static final String Authorize_UserName = "X-Authorize-UserName";

    public AuthorizeGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("enabled");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!config.isEnabled()) {
                return chain.filter(exchange);
            }

            ServerHttpRequest request = exchange.getRequest();

            // 获取cookie中的身份令牌
            Map<String, String> cookie = this.readCookie(request, "utoken");
            String utoken = cookie.get("utoken");

            if (StringUtils.isEmpty(utoken)) {
                return this.responseDenied(CommonCode.UNAUTHENTICATED, exchange, chain);
            }

            // 这里只校验是否携带jwt令牌，令牌有效性由各资源服务（微服务）进行校验
            String jwt = this.getAuthorizationHeader(request);
            if (StringUtils.isEmpty(jwt)) {
                return this.responseDenied(CommonCode.NOTOKEN, exchange, chain);
            }

            // 校验用户令牌是否过期
            Long expire = this.stringRedisTemplate.getExpire("user:token:" + utoken);
            if (expire < 0) {
                return this.responseDenied(CommonCode.TOKENEXPIRES, exchange, chain);
            }

            // 解开jwt令牌，获取用户id和用户名信息
            Oauth2Util.UserJwt userJwt = Oauth2Util.decodeUserJwt(jwt);

            // 设置请求头供后续使用
            // userId
            // userName
            ServerHttpRequest buildrequest = exchange.getRequest().mutate()
                    .headers(httpHeaders -> httpHeaders.set(Authorize_UserName, userJwt.getName()))
                    .headers(httpHeaders -> httpHeaders.set(Authorize_UserID, userJwt.getId()))
                    .build();

            return chain.filter(exchange.mutate().request(buildrequest).build());
        };
    }

    public static class Config {
        // 控制是否开启认证
        private boolean enabled;

        public Config() {
        }

        public boolean isEnabled() {
            return enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    }

    private String getAuthorizationHeader(ServerHttpRequest servletRequest) {
        HttpHeaders headers = servletRequest.getHeaders();
        String authorization = headers.getFirst("Authorization");
        if (StringUtils.isEmpty(authorization)) {
            return null;
        }

        if (!authorization.startsWith("Bearer ")) {
            return null;
        }

        return authorization.substring(7);
    }

    public static Map<String, String> readCookie(ServerHttpRequest request, String... cookieNames) {
        Map<String, String> cookieMap = new HashMap<String, String>();
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        if (cookies != null) {

            Iterator<Map.Entry<String, List<HttpCookie>>> iterator = cookies.entrySet().iterator();

            while (iterator.hasNext()) {
                Map.Entry<String, List<HttpCookie>> next = iterator.next();
                String cookieName = next.getKey();
                List<HttpCookie> value = next.getValue();
                if (!StringUtils.isEmpty(value)) {
                    value.stream().forEach(x -> {
                        for (int i = 0; i < cookieNames.length; i++) {
                            if (cookieNames[i].equals(cookieName)) {
                                cookieMap.put(x.getName(), x.getValue());
                            }
                        }
                    });
                }
            }
        }
        return cookieMap;
    }

    private Mono<Void> responseDenied(CommonCode commonCode, ServerWebExchange exchange, GatewayFilterChain chain) {

        //构建统一响应结果
//        ServerHttpResponse originalResponse = exchange.getResponse();
//        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
//        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
//            @Override
//            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
//                if (body instanceof Flux) {
//                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
//                    return super.writeWith(fluxBody.map(dataBuffer -> {
//                        // probably should reuse buffers
////                        byte[] content = new byte[dataBuffer.readableByteCount()];
////                        dataBuffer.read(content);
//                        // 释放掉内存
////                        DataBufferUtils.release(dataBuffer);
////                        String rs = new String(content, Charset.forName("UTF-8"));
//                        ResponseResult responseResult = new ResponseResult(commonCode);
//                        byte[] newRs = JSON.toJSONString(responseResult).getBytes(Charset.forName("UTF-8"));
//                        this.getDelegate().getHeaders().setContentLength(newRs.length);//如果不重新设置长度则收不到消息。
//                        return bufferFactory.wrap(newRs);
//                    }));
//                }
//                // if body is not a flux. never got there.
//                return super.writeWith(body);
//            }
//        };
//        // replace response with decorator
////        exchange.mutate().response(decoratedResponse);
////        return originalResponse.setComplete();
//        return chain.filter(exchange.mutate().response(decoratedResponse).build());


        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {

            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                ResponseResult responseResult = new ResponseResult(commonCode);
                byte[] newRs = JSON.toJSONString(responseResult).getBytes(Charset.forName("UTF-8"));
                bufferFactory.wrap(newRs);
                this.getDelegate().getHeaders().setContentLength(newRs.length);
                log.info("降级处理返回数据：{}" + body);
                return super.writeWith(body);
            }

        };

        ServerHttpRequest buildrequest = exchange.getRequest().mutate()
                .headers(httpHeaders -> httpHeaders.set(Authorize_UserName, exchange.getRequest().getQueryParams().get("name").get(0)))
                .build();

        return chain.filter(exchange.mutate().request(buildrequest).response(decoratedResponse).build());
    }
}
